package com.rightrule.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.rightrule.rule.constans.CommonConstant;
import com.rightrule.rule.constans.DecisionStatusEnum;
import com.rightrule.rule.dto.*;
import com.rightrule.service.DecisionService;
import com.rightrule.service.FieldComputeService;
import com.rightrule.service.StrategyDecisionService;
import com.rightrule.service.StrategyRouterService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
@Slf4j
public class DecisionServiceImpl implements DecisionService {

    @Autowired
    private StrategyRouterService strategyRouterService;

    @Autowired
    private FieldComputeService fieldComputeService;

    @Autowired
    private StrategyDecisionService strategyDecisionService;

    private final int DEFAULT_RESULT_SIZE = 2;

    @Override
    public DecisionResultDTO doDecision(CommonDecisionDTO commonDecisionReq) {

        RouterReqDTO routerReqDTO = new RouterReqDTO();
        BeanUtils.copyProperties(commonDecisionReq, routerReqDTO);
        RouterResultDTO routerResult = null;
        DecisionResultDTO decisionResultDTO = new DecisionResultDTO();

        decisionResultDTO.setDecisionStatus(DecisionStatusEnum.SUCCESS.getCode());

        try {
            routerResult = strategyRouterService.router(routerReqDTO);
        } catch (Exception e) {
            log.error("strategy router exception,routerReq={},ex={}", routerReqDTO, e);
            throw new RuntimeException("strategy router version exception");
        }

        log.info("router success, request = {},routerResult={},ready to decision", commonDecisionReq, routerResult);

        // 检测需要带入的事件变量是否存在
        checkEventFields(routerResult.getEventFields(), commonDecisionReq);

        // 筛选出业务已经传下来的字段，留下需要去拉取的字段
        List<DecisionFieldDTO> dependField = routerResult.getInputFields();
        HashMap<String, String> dependFieldErrorHandleTypes = new HashMap<>();
        // 需要拉取的变量值的变量
        ArrayList<String> requestFields = new ArrayList<>();
        for (DecisionFieldDTO fieldDTO : dependField) {
            if (!commonDecisionReq.getSessionData().containsKey(fieldDTO.getFieldName())) {
                requestFields.add(fieldDTO.getFieldName());
                dependFieldErrorHandleTypes.put(fieldDTO.getFieldName(), fieldDTO.getErrorHandleType());
            }
        }

        HashMap<String, Object> sessionData = new HashMap<>();
        // 去拉取变量
        if (!CollectionUtils.isEmpty(requestFields)) {
            CommonFieldComputeDTO commonFieldComputeDTO = new CommonFieldComputeDTO();
            commonFieldComputeDTO.setFieldNameList(requestFields);
            commonFieldComputeDTO.setIpId(commonDecisionReq.getIpId());
            commonFieldComputeDTO.setBusinessId(commonDecisionReq.getBusinessId());
            commonFieldComputeDTO.setSessionData(commonDecisionReq.getSessionData());
            commonFieldComputeDTO.setScriptData(commonDecisionReq.getStrategyCode());
            JSONObject computeResult = fieldComputeService.compute(commonFieldComputeDTO);
            if (computeResult.isEmpty()) {
                throw new RuntimeException("compute requestFields is null");
            }

            // 如果缺少字段
            if ((computeResult.size() - DEFAULT_RESULT_SIZE) < requestFields.size()) {
                log.error("lack field,reqFields = {},returnFields = {}", requestFields, computeResult);
            }

            // 追加拉取的变量
            sessionData.putAll(computeResult);

            // 需要向特征平台拉取到的变量集
            if (commonDecisionReq.getIsNeedReturnFeatures()) {
                decisionResultDTO.setFeatures(computeResult);
            }

            // 如果有字段拉取异常则需要处理拉取异常的字段
            if (!DecisionStatusEnum.SUCCESS.getCode().equals(computeResult.getString("success"))) {
                JSONArray errorFieldArray = computeResult.getJSONArray("fail");
                for (int i = 0; i < errorFieldArray.size(); i++) {
                    // 如果当前字段的缺失是需要重试时，当前决策状态改为重试，
                    // todo 稍后定时任务拉取需要重试的决策状态进行重试，设置重试次数超过重试次数则默认为决策失败
                    if ("pending".equals(dependFieldErrorHandleTypes.get(i))) {
                        decisionResultDTO.setDecisionStatus("pending");
                        return decisionResultDTO;
                    }
                }
            }
        }

        try {
            JSONObject features = decisionResultDTO.getFeatures();
            // 把拉取的变量和决策带入的变量合并
            Map<String, Object> inputSessionData = commonDecisionReq.getSessionData();
            inputSessionData.putAll(sessionData);
            sessionData.put("ipId", commonDecisionReq.getIpId());
            sessionData.put("businessId", commonDecisionReq.getBusinessId());
            commonDecisionReq.setSessionData(inputSessionData);
            commonDecisionReq.setStrategyVersionId(routerResult.getStrategyVersionId());

            log.info("begin decision reqDTO={}", commonDecisionReq);

            decisionResultDTO = strategyDecisionService.decision(commonDecisionReq);
            decisionResultDTO.setDecisionStatus(DecisionStatusEnum.SUCCESS.getCode());
            decisionResultDTO.setFeatures(features);

            log.info("decision success result = {}", decisionResultDTO);
            decisionResultDTO.setStrategyVersionId(routerResult.getStrategyVersionId());

        } catch (Exception e) {
            log.error("decision occurred exception,e = {}", e);
            decisionResultDTO.setDecisionStatus(DecisionStatusEnum.ERROR.getCode());
        }

        return decisionResultDTO;
    }


    /**
     * 检查必填的事件变量是否存在
     *
     * @param eventFields
     */
    private void checkEventFields(List<DecisionFieldDTO> eventFields, CommonDecisionDTO commonDecisionReq) {
        for (DecisionFieldDTO eventField : eventFields) {
            if (CommonConstant.EVENT_INPUT_IPID.equals(eventField.getFieldName())
                    && StringUtils.isEmpty(commonDecisionReq.getIpId())) {
                log.error("depend fieldName = {} empty,commonDecisionReq = {}", eventField.getFieldName(), commonDecisionReq);
                throw new RuntimeException("param exception");
            }

            if (CommonConstant.EVENT_INPUT_BUSINESS_ID.equals(eventField.getFieldName())
                    && StringUtils.isEmpty(commonDecisionReq.getBusinessId())) {
                log.error("depend fieldName = {} empty,commonDecisionReq = {}", eventField.getFieldName(), commonDecisionReq);
                throw new RuntimeException("param exception");
            }

            if (!CommonConstant.EVENT_INPUT_IPID.equals(eventField.getFieldName())
                    && !CommonConstant.EVENT_INPUT_BUSINESS_ID.equals(eventField.getFieldName())
                    && !commonDecisionReq.getSessionData().containsKey(eventField.getFieldName())
            ) {
                log.error("depend fieldName = {} empty,commonDecisionReq = {}", eventField.getFieldName(), commonDecisionReq);
                throw new RuntimeException("param exception");
            }
        }
    }
}
